package mylogger

import (
	"fmt"
	"os"
	"path"
	"time"
)

// FileLogger 结构体
type FileLogger struct {
	Level       LogLevel
	filePath    string
	fileName    string
	fileObj     *os.File
	errFileObj  *os.File
	maxFileSize int64
}

// NewFileLogger  构造函数
func NewFileLogger(levelstr, filePath, fileName string, maxFileSize int64) *FileLogger {

	logLevel, err := parseLogLevel(levelstr)
	if err != nil {
		panic(err)
	}
	nfl := &FileLogger{
		Level:       logLevel,
		filePath:    filePath,
		fileName:    fileName,
		maxFileSize: maxFileSize,
	}

	err = nfl.initFile()
	if err != nil {
		panic(err)
	}

	return nfl

}
func (f *FileLogger) checkFileSize(fileObj *os.File) bool {

	fileInfo, err := fileObj.Stat()
	if err != nil {
		fmt.Printf("Get File Stat Error, %v", err)
		return false
	}
	fmt.Println(fileInfo.Size(), f.maxFileSize)
	return fileInfo.Size() > f.maxFileSize
}

func (f *FileLogger) splitFile(file *os.File) (*os.File, error) {
	fileInfo, err := file.Stat()
	fileName := fileInfo.Name()
	for i := 5; i >= 1; i-- {
		oldFileName := fmt.Sprintf("%s.%d", fileName, i)
		fullOldFilePath := path.Join(f.filePath, oldFileName)
		_, err := os.Stat(fullOldFilePath)
		if err == nil {
			if i == 5 {
				os.Remove(fullOldFilePath)
			}
			if i < 5 {
				moveFileName := fmt.Sprintf("%s.%d", fileName, i+1)
				os.Rename(fullOldFilePath, path.Join(f.filePath, moveFileName))
			}
		}
	}
	fullFilePath := path.Join(f.filePath, fileName)
	newFullFilePath := path.Join(f.filePath, fileName+".1")
	file.Close()
	fError := os.Rename(fullFilePath, newFullFilePath)
	fmt.Println(fError, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
	// 3. 打开一个新的日志文件
	newfileObj, err := os.OpenFile(fullFilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("open new log file failed, err:%v\n", err)
		return nil, err
	}
	// 4. 将打开的新日志文件对象赋值给 f.fileObj
	return newfileObj, nil

}

func (f *FileLogger) initFile() error {
	errFileName := fmt.Sprintf("Error_%s", f.fileName)
	fullFilePath := path.Join(f.filePath, f.fileName)
	fileObj, err := os.OpenFile(fullFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)

	if err != nil {
		fmt.Printf("open log file failed ,err:%v \n", err)
		return err
	}
	f.fileObj = fileObj

	errFileObj, err := os.OpenFile(path.Join(f.filePath, errFileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)

	if err != nil {
		fmt.Printf("open err log file failed ,err:%v \n", err)
		return err
	}
	f.errFileObj = errFileObj

	return nil
}

func (f *FileLogger) log(lv LogLevel, format string, skip int, a ...interface{}) {
	if lv >= f.Level {
		funcName, fileName, lineNo := getInfo(skip)
		msg := fmt.Sprintf(format, a...)
		if f.checkFileSize(f.fileObj) {
			newFileObj, err := f.splitFile(f.fileObj)
			if err != nil {
				fmt.Printf("Log  error:%v\n", err)
				panic(err)
			}
			f.fileObj = newFileObj
		}

		_, _ = fmt.Fprintf(
			f.fileObj,
			"[%s] [%s]\t%s:%s:%d\t%s \n",
			time.Now().Format("2006-01-02 15:04:05.000"),
			getLogLevelstr(lv),
			funcName,
			fileName,
			lineNo, msg)
		if lv >= ERROR{
			if f.checkFileSize(f.errFileObj) {
				newFileObj, err := f.splitFile(f.errFileObj)
				if err != nil {
					fmt.Printf("Log  error:%v\n", err)
					panic(err)
				}
				f.errFileObj = newFileObj
			}

			_, _ = fmt.Fprintf(
				f.errFileObj,
				"[%s] [%s]\t%s:%s:%d\t%s \n",
				time.Now().Format("2006-01-02 15:04:05.000"),
				getLogLevelstr(lv),
				funcName,
				fileName,
				lineNo, msg)
		}

	}

}

// Debug 日志
func (f *FileLogger) Debug(format string, skip int, a ...interface{}) {
	f.log(DEBUG, format, skip, a...)
}

func (f *FileLogger) Info(format string, skip int, a ...interface{}) {
	f.log(INFO, format, skip, a...)
}

func (f *FileLogger) Warning(format string, skip int, a ...interface{}) {
	f.log(WARNING, format, skip, a...)
}
func (f *FileLogger) Error(format string, skip int, a ...interface{}) {
	f.log(ERROR, format, skip, a...)
}

func (f *FileLogger) Fatal(format string, skip int, a ...interface{}) {
	f.log(FATAL, format, skip, a...)
}

// Close 关闭日志

func (f *FileLogger) Close() {
	err1 := f.fileObj.Close()
	err2 := f.errFileObj.Close()
	fmt.Println(err1, err2)

}
